package rmi.billing.server;

import java.rmi.RemoteException;
import java.util.ArrayList;
import org.apache.log4j.Logger;
import rmi.billing.domain.Bill;
import rmi.billing.domain.BillPosition;
import rmi.billing.domain.EndedAuction;
import rmi.billing.domain.PriceStep;
import rmi.billing.domain.PriceSteps;
import rmi.billing.exceptions.InvalidPriceStepRemoteException;
import rmi.billing.exceptions.NegativeValuesRemoteException;
import rmi.billing.exceptions.PriceIntervallCollisionRemoteException;
import rmi.billing.exceptions.PriceStepNotExistsRemoteException;

/*
 * computes the command from the ManagementClient regarding PriceSteps and Bill
 */
public class BillingServerSecureImpl implements BillingServerSecure{

	private static final long serialVersionUID = 7886949932646434172L;
	public static Logger log = Logger.getLogger(BillingServerSecureImpl.class);
	private PriceSteps priceSteps = new PriceSteps();
	private ArrayList<EndedAuction> endedAuctions = new ArrayList<EndedAuction>();
	
	public BillingServerSecureImpl() {
		
	}

	@Override
	public PriceSteps getPriceSteps() {
		//returns available PriceSteps
		synchronized(this.priceSteps.getSteps()){
			return this.priceSteps;
		}
	
	}

	@Override
	public void createPriceStep(double startPrice, double endPrice,	double fixedPrice, double variablePricePercent) 
			throws RemoteException,NegativeValuesRemoteException,InvalidPriceStepRemoteException,InvalidPriceStepRemoteException,PriceIntervallCollisionRemoteException {
		
		// if any of the specified values is negative throw a RemoteException
		if(startPrice<0 || endPrice<0 || fixedPrice<0 || variablePricePercent<0)
			throw new NegativeValuesRemoteException();
		
		// if startPrice greater than endPrice (if not 0) throw a RemoteException
		if(startPrice > endPrice && endPrice!=0)
			throw new InvalidPriceStepRemoteException();
		
		// if startPrice and endPrice are 0 throw a RemoteException
		if(startPrice==0 && endPrice==0)
			throw new InvalidPriceStepRemoteException();
		
		//if the provided price interval collides (overlaps) with an existing price step throw a RemoteException 
		if(checkCollision(startPrice,endPrice))		
			throw new PriceIntervallCollisionRemoteException();
		
		//if all the values are valid and no collision occured the PriceStep is added
		synchronized(this.priceSteps.getSteps()){
			this.priceSteps.addPriceStep(new PriceStep(startPrice, endPrice,fixedPrice, variablePricePercent));
		}
		
		log.info("PriceStep ["+startPrice+" "+endPrice+"] created");
		
		
	}
	public boolean checkCollision(double startPrice, double endPrice){
		//Pricestep already exists
		if(this.getPriceSteps().getPriceStep(startPrice, endPrice)!=null)	
			return true;
		
		//Pricestep overlaps
		for(PriceStep ps : priceSteps.getSteps()){
			if (startPrice>=ps.getStartPrice() && (startPrice<ps.getEndPrice() || ps.getEndPrice()==0 ))
				return true;
			if (startPrice<=ps.getStartPrice() && (endPrice>ps.getStartPrice() || endPrice==0 ) )
				return true;						
		}
		
		return false;
	}

	@Override
	public void deletePriceStep(double startPrice, double endPrice) throws RemoteException,PriceStepNotExistsRemoteException {
		//if the PriceStep is available it will be deleted
		synchronized(this.priceSteps.getSteps()){
			PriceStep ps = this.priceSteps.getPriceStep(startPrice, endPrice);
			if(ps!=null){				
				this.priceSteps.removePriceStep(ps);
				log.info("PriceStep ["+startPrice+" "+endPrice+"] removed");
			}
			else{
				log.info("PriceStep ["+startPrice+" "+endPrice+"] does not exist");
				throw new PriceStepNotExistsRemoteException();
			}
		}
	}

	@Override
	public void billAuction(String user, long auctionID, double price) {
		//this method is called by the AuctionServer everytime an auction ends and the auction will be stored in a arraylist
		synchronized(endedAuctions){
			this.endedAuctions.add(new EndedAuction(user,auctionID,price));
		}
	}

	@Override
	public Bill getBill(String user) {
		//a bill from the given user is generated and returned to the managment client
		Bill bill = new Bill();
		
		synchronized(endedAuctions){
			for(EndedAuction ea : endedAuctions){
				if (ea.getUser().equals(user)){					
					bill.addBillPositions(generateBillPosition(user,ea));
				}
			}
		}
		
		return bill;
	}
	
	public BillPosition generateBillPosition(String user, EndedAuction ea){
		//the fees from each auction in the bill are calculated based on the existing PriceSteps
		Double feeVariable = 0.0;
		Double feeFixed = 0.0;
		
		synchronized(this.priceSteps.getSteps()){
		
			for(PriceStep ps : priceSteps.getSteps()){
				if (ea.getPrice()>=ps.getStartPrice() && (ea.getPrice()<=ps.getEndPrice() || ps.getEndPrice()==0 ) ){
					//calculate fees
					feeVariable=ea.getPrice()*ps.getVariablePricePercent()/100;
					feeFixed=ps.getFixedPrice();
				}
			}
		
		}
		return new BillPosition(ea.getAuctionID(), ea.getPrice(), feeFixed, feeVariable, feeFixed+feeVariable);
	}

}
